#include <string>
#include <vector>
#include <sstream>
#include <chrono>
#include <thread>
#include <cstdlib>
#include "sys/types.h"
#include <unistd.h>

using namespace std;
using namespace std::chrono_literals;

#include "./TestSetBase.h"
#include "./BenchmarkWorkloadBase.h"
#include "./common_config.h"


void BenchmarkWorkloadBase::operator()(TestSetBase *testctx)
{
    volatile bool start_barrier = false;
    volatile bool end_barrier = false;

    /* start the workload */
    const auto __thread_run = [&] (unsigned thread_id)
    {
        while (!start_barrier) ;

        for (unsigned run; !end_barrier; run++) {
            workload(testctx, thread_id, run);
            /* PERF (zxg) may cause head-of-line blocking due to memory
                barrier
                Experiment shows latency introduced by framework increased
                from 9.9e-3us to 2.7us with thread count ranging from 1 to 64
                (see README.md#Performance), which is negligible in terms of
                absolute value. */
            executed_times.fetch_add(1, std::memory_order_release);
        }
    };

    vector<std::thread> tpool;
    for (unsigned thread_id = 0; thread_id < thread_nr; thread_id++)
        tpool.push_back(std::thread(__thread_run, thread_id));

    const auto start_time = std::chrono::steady_clock::now();
    start_barrier = true;

    /* perf record */
    const auto __thread_perf = [&]
    {
        /* record perf of this benchmark */
        {
            ostringstream cmd;
            cmd << "perf record -F 99 -g "
                // << "-p `pidof rpmemd | sed 's/ /,/g'` "
                << "-p " << getpid() << " "
                << "-- sleep " << minimum_time.count();
            std::system(cmd.str().c_str());
        }
        /* dump to flamegraph */
        string outfile_wle;
        {
            ostringstream cmd;
            cmd << "perf-" << name << "_thrd" << thread_nr;
            outfile_wle = cmd.str();
        }
        {
            ostringstream cmd;
            cmd << "perf script -i perf.data > " << outfile_wle << ".unfold";
            std::system(cmd.str().c_str());
        }
        {
            ostringstream cmd;
            cmd << common_config.perf_tools_path << "FlameGraph/stackcollapse-perf.pl "
                << outfile_wle << ".unfold" << " > " << outfile_wle << ".folded";
            std::system(cmd.str().c_str());
        }
        {
            ostringstream cmd;
            cmd << common_config.perf_tools_path << "FlameGraph/flamegraph.pl "
                << outfile_wle << ".folded" << " > " << outfile_wle << ".svg";
            std::system(cmd.str().c_str());
        }
    };
    // tpool.push_back(std::thread(__thread_perf));

    /* finish */
    while (executed_times.load(std::memory_order_acquire) < minimum_runs
            || std::chrono::steady_clock::now() - start_time < minimum_time) {
        std::this_thread::sleep_for(1.0s);
    }
    const auto end_time = std::chrono::steady_clock::now();
    end_barrier = true;
    executed_timespan = end_time - start_time;

    for (auto &w : tpool)
        w.join();
}
